#ifndef __REFERENCED_H__
#define __REFERENCED_H__

//#include "OgrePlatform.h"
#include "kernel/Prerequisites_kernel.h"
#include "kernel/dll_cfg.h"
//#include "threading/OgreThreaddefines.h"
#include "threading/Atomic.h"

#define _OSG_REFERENCED_USE_ATOMIC_OPERATIONS

namespace FD
{
	namespace Kernel
	{
		// forward declare, declared after CReferenced below.
		class CDeleteHandler;
		class CObserver;
		class ObserverSet;
		class Mutex;
		//{
		//public:
		//	void lock()
		//	{};

		//	void unlock()
		//	{};
		//};

		/** template class to help enforce static initialization order. */
		template <typename T, T M()>
		struct depends_on
		{
			depends_on() { M(); }
		};

		/** Base class for providing reference counted objects.*/
		class FD_Kernel_Export CReferenced
		{
		public:

			CReferenced();

			explicit CReferenced(bool threadSafeRefUnref);

			CReferenced(const CReferenced&);

			inline CReferenced& operator = (const CReferenced&) { return *this; }

			/** Set whether to use a mutex to ensure ref() and unref() are thread safe.*/
			virtual void setThreadSafeRefUnref(bool threadSafe);

			/** Get whether a mutex is used to ensure ref() and unref() are thread safe.*/

#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
			bool getThreadSafeRefUnref() const { return true; }
#else
			bool getThreadSafeRefUnref() const { return _refMutex!=0; }
#endif

			/** Get the mutex used to ensure thread safety of ref()/unref(). */
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
			Mutex* getRefMutex() const { return getGlobalReferencedMutex(); }
#else
			Mutex* getRefMutex() const { return _refMutex; }
#endif

			/** Get the optional global CReferenced mutex, this can be shared between all FD::Kernel::CReferenced.*/
			static Mutex* getGlobalReferencedMutex();

			/** Increment the reference count by one, indicating that
			this object has another pointer which is referencing it.*/
			inline int ref() const;

			/** Decrement the reference count by one, indicating that
			a pointer to this object is no longer referencing it.  If the
			reference count goes to zero, it is assumed that this object
			is no longer referenced and is automatically deleted.*/
			inline int unref() const;

			/** Decrement the reference count by one, indicating that
			a pointer to this object is no longer referencing it.  However, do
			not delete it, even if ref count goes to 0.  Warning, unref_nodelete()
			should only be called if the user knows exactly who will
			be responsible for, one should prefer unref() over unref_nodelete()
			as the latter can lead to memory leaks.*/
			int unref_nodelete() const;

			/** Return the number of pointers currently referencing this object. */
			inline int referenceCount() const { return _refCount; }

			inline void setReferenceCount(const int& refCount)
			{
				int val = refCount;
				while(val > 0)
				{
					++_refCount;
					--val;
				}
			}

			/** Get the ObserverSet if one is attached, otherwise return NULL.*/
			ObserverSet* getObserverSet() const
			{
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
				return static_cast<ObserverSet*>(_observerSet.get());
#else
				return static_cast<ObserverSet*>(_observerSet);
#endif
			}

			/** Get the ObserverSet if one is attached, otherwise create an ObserverSet, attach it, then return this newly created ObserverSet.*/
			ObserverSet* getOrCreateObserverSet() const;

			/** Add a CObserver that is observing this object, notify the CObserver when this object gets deleted.*/
			void addObserver(CObserver* observer) const;

			/** Remove CObserver that is observing this object.*/
			void removeObserver(CObserver* observer) const;

		public:

			/** Set whether reference counting should use a mutex for thread safe reference counting.*/
			static void setThreadSafeReferenceCounting(bool enableThreadSafeReferenceCounting);

			/** Get whether reference counting is active.*/
			static bool getThreadSafeReferenceCounting();

			friend class CDeleteHandler;

			/** Set a CDeleteHandler to which deletion of all referenced counted objects
			* will be delegated.*/
			void setDeleteHandler(CDeleteHandler* handler);

			/** Get a CDeleteHandler.*/
			CDeleteHandler* getDeleteHandler()const;


		protected:

			virtual ~CReferenced();

			void signalObserversAndDelete(bool signalDelete, bool doDelete) const;

			void deleteUsingDeleteHandler() const;

			CDeleteHandler* _deleteHandler;

#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
			mutable FD::Kernel::CAtomicPtr  _observerSet;
			mutable FD::Kernel::CAtomic     _refCount;
#else
			mutable Mutex*		_refMutex;
			mutable int              _refCount;
			mutable void*          _observerSet;
#endif
		};

		inline int CReferenced::ref() const
		{
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
			return ++_refCount;
#else
			if (_refMutex)
			{
				OpenThreads::ScopedLock<Mutex> lock(*_refMutex);
				return ++_refCount;
			}
			else
			{
				return ++_refCount;
			}
#endif
		}

		inline int CReferenced::unref() const
		{
			int newRef;
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
			newRef = --_refCount;
			bool needDelete = (newRef == 0);
#else
			bool needDelete = false;
			if (_refMutex)
			{
				OpenThreads::ScopedLock<Mutex> lock(*_refMutex);
				newRef = --_refCount;
				needDelete = newRef==0;
			}
			else
			{
				newRef = --_refCount;
				needDelete = newRef==0;
			}
#endif

			if (needDelete)
			{
				signalObserversAndDelete(true,true);
			}
			return newRef;
		}

		// intrusive_ptr_add_ref and intrusive_ptr_release allow
		// use of osg CReferenced classes with boost::intrusive_ptr
		inline void intrusive_ptr_add_ref(CReferenced* p) { p->ref(); }
		inline void intrusive_ptr_release(CReferenced* p) { p->unref(); }
	}
}

#endif